﻿using System.Collections.Generic;
using System.Linq;

namespace FAQ_UI
{
	public abstract class LruCache<T>
	{
		private Dictionary<string, T> cache = new Dictionary<string, T>();
		private Dictionary<string, long> cacheTime = new Dictionary<string, long>();
		private long limit;
		private long timeLimit;

		public LruCache(long limit, long timeLimit)
		{
			this.limit = limit;
			this.timeLimit = timeLimit;
		}

		public bool has(string key)
		{
			return cache.ContainsKey(key);
		}

		public void put(string key, T value)
		{
			if (value != null) checkSize(value);
			checkTime();
			updateTime(key);
			cache[key] = value;
		}

		public void updateTime(string key)
		{
			cacheTime[key] = java.lang.System.currentTimeMillis();
		}

		public void checkSize(T newValue)
		{
			long sum = 0;
			foreach (T v in cache.Values)
			{
				sum += getSize(v);
			}
			sum += getSize(newValue);
			while (sum > limit)
			{
				T remove = removeLeastUsed();
				sum -= getSize(remove);
			}
		}

		public void checkTime()
		{
			foreach (var v in cacheTime.AsEnumerable().ToArray())
			{
				if (java.lang.System.currentTimeMillis() - v.Value > timeLimit)
				{
					string key = v.Key;
					cacheTime.Remove(key);
					cache.Remove(key);
				}
			}
		}

		public T removeLeastUsed()
		{
			IEnumerable<KeyValuePair<string, long>> it = cacheTime.AsEnumerable();
			string least = null;
			long min = long.MaxValue;
			foreach (var i in it)
			{
				if (i.Value < min)
				{
					min = i.Value;
					least = i.Key;
				}
			}
			T val = cache[least];
			cacheTime.Remove(least);
			cache.Remove(least);
			return val;
		}

		public abstract long getSize(T obj);

		public void remove(string key)
		{
			cache.Remove(key);
			cacheTime.Remove(key);
		}

		public T get(string key)
		{
			updateTime(key);
			return cache[key];
		}

		public T get(string key, Loader<T> loader)
		{
			if (!has(key)) put(key, loader());
			T val=get(key);
			if (val is IMutatable<T>) return ((IMutatable<T>)val).mutate();
			return val;
		}
		public void clear()
		{
			cacheTime.Clear();
			cache.Clear();
		}
	}

	public delegate T Loader<T>();

	public class DrawableLruCache : LruCache<Drawable>
	{
		public DrawableLruCache(long limit, long timeLimit) : base(limit, timeLimit)
		{
		}

		public override long getSize(Drawable obj)
		{
			if (obj is BitmapDrawable)
			{
				BitmapDrawable bitmapSource = (BitmapDrawable)obj;
				return bitmapSource.Format.BitsPerPixel * bitmapSource.PixelWidth * bitmapSource.PixelHeight / 8;
			}
			return 0;
		}
	}

	public class NinePatchLruCache : LruCache<NinePatchDrawable>
	{
		public NinePatchLruCache(long limit, long timeLimit) : base(limit, timeLimit)
		{
		}

		public override long getSize(NinePatchDrawable obj)
		{
			if (obj is NinePatchDrawable)
			{
				NinePatchDrawable bitmapSource = (NinePatchDrawable)obj;
				return bitmapSource.bitmap.Format.BitsPerPixel * bitmapSource.bitmap.PixelWidth * bitmapSource.bitmap.PixelHeight / 8;
			}
			return 0;
		}
	}
}